package ph.com.gs3.chat.services;

import ph.com.gs3.chat.ChatActivity;
import ph.com.gs3.chat.R;
import ph.com.gs3.chat.managers.ImageManager;
import ph.com.gs3.chat.managers.PendingMessagesManager;
import ph.com.gs3.chat.managers.RealTimeEventsManager;
import ph.com.gs3.chat.managers.RealTimeEventsManager.RealTimeEventsManagerListener;
import ph.com.gs3.chat.model.valueobject.business.Message;
import ph.com.gs3.chat.model.valueobject.business.User;
import ph.com.gs3.chat.model.valueobject.business.UserStub;
import ph.com.gs3.chat.utilities.FLLogger;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Vibrator;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;

public class NotifierService extends Service implements RealTimeEventsManagerListener {

	// ===============================================================================
	// {{ Constants
	public static final String TAG = NotifierService.class.getSimpleName();
	public static final String NAME = NotifierService.class.getName();

	// broadcasted actions
	public static final String ACTION_ON_CONNECTED = NAME + "_on_connected";
	public static final String ACTION_ON_DISCONNECTED = NAME + "_on_disconnected";
	public static final String ACTION_ON_USER_LOGGED_IN = NAME + "_on_user_logged_in";
	public static final String ACTION_ON_USER_LOGGED_OUT = NAME + "_on_user_logged_out";
	public static final String ACTION_ON_NOTIFICATION_RECEIVED = NAME + "_on_notification_received";
	public static final String ACTION_ON_MESSAGE_RECEIVED = NAME + "_on_message_received";

	// incoming extras
	public static final String EXTRA_PORT = "port";
	public static final String EXTRA_CURRENT_USER = "current_user";

	// outgoing extras
	public static final String EXTRA_LOGGED_IN_USER = "logged_in_user";
	public static final String EXTRA_LOGGED_OUT_USER = "logged_out_user_id";
	public static final String EXTRA_RECIEVED_MESSAGE = "recieved_message";

	// }}

	private static boolean serviceRunning = false;

	private int socketServerPort;
	private User currentUser;

	// ===============================================================================
	// {{ Service Life Cycle Implementation Methods

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {

		Bundle extras = intent.getExtras();

		if (!serviceRunning) {
			// start the service
			startService(extras);

			FLLogger.d(TAG, "Started");
		} else {

			// restart the service
			stopService();
			startService(extras);

			FLLogger.d(TAG, "Restarted");
		}

		return START_REDELIVER_INTENT;
	}

	@Override
	public void onDestroy() {
		super.onDestroy();

		stopService();
		FLLogger.d(TAG, "Service destroyed");
	}

	@Override
	public void onLowMemory() {
		super.onLowMemory();

		FLLogger.w(TAG, "Running out of memory");
	}

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	// }}

	// ===============================================================================
	// {{ Functional Methods

	private void startService(Bundle extras) {
		initializeAndValidateParametersToTemp(extras);
		RealTimeEventsManager.getDefaultInstance().connect(currentUser, socketServerPort);
		RealTimeEventsManager.getDefaultInstance().registerListner(this);
		serviceRunning = true;
	}

	private void stopService() {

		RealTimeEventsManager.getDefaultInstance().unregisterListener(this);
		RealTimeEventsManager.getDefaultInstance().disconnect();
		serviceRunning = false;

	}

	private void initializeAndValidateParametersToTemp(Bundle extras) {

		IllegalArgumentException invalidPort = new IllegalArgumentException(
		        "The "
		                + EXTRA_PORT
		                + " is invalid, it must be a positive integer representing the port that the socket server is on. Ex. 3013");

		IllegalArgumentException missingUser = new IllegalArgumentException("The "
		        + EXTRA_CURRENT_USER + " must be supplied to this service before starting it");

		// user
		currentUser = (User) extras.getSerializable(EXTRA_CURRENT_USER);
		if (currentUser == null) {
			throw missingUser;
		}

		// port
		socketServerPort = extras.getInt(EXTRA_PORT, -1);
		if (socketServerPort <= 0) {
			throw invalidPort;
		}

	}

	// }}

	// ===============================================================================
	// {{ Real Time Events Manager Implementation Methods

	@Override
	public void onConnected() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onRegistered() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onDisconnected() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onUserLoggedIn(UserStub user) {
		Intent broadcastIntent = new Intent();
		broadcastIntent.setAction(ACTION_ON_USER_LOGGED_IN);
		broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);

		broadcastIntent.putExtra(EXTRA_LOGGED_IN_USER, user);

		sendBroadcast(broadcastIntent);
		FLLogger.d(TAG, "Broadcasted: " + ACTION_ON_USER_LOGGED_IN);
	}

	@Override
	public void onUserLoggedOut(UserStub user) {
		Intent broadcastIntent = new Intent();
		broadcastIntent.setAction(ACTION_ON_USER_LOGGED_OUT);
		broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);

		broadcastIntent.putExtra(EXTRA_LOGGED_OUT_USER, user);

		sendBroadcast(broadcastIntent);

	}

	@Override
	public void onMessageReceived(Message message) {
		Intent broadcastIntent = new Intent();
		broadcastIntent.setAction(ACTION_ON_MESSAGE_RECEIVED);
		broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);

		broadcastIntent.putExtra(EXTRA_RECIEVED_MESSAGE, message);

		sendBroadcast(broadcastIntent);

		displayNewMessageNotification(message);

		Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
		vibrator.vibrate(1500);
	}

	// }}

	// ===============================================================================
	// {{ Notification Methods

	public void displayNewMessageNotification(Message message) {

		FLLogger.d(TAG, "Request to display message notification received");

		// Builder notification
		NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

		UserStub messageAuthor = message.getAuthor();

		builder.setAutoCancel(true);
		builder.setContentTitle(messageAuthor.getDisplayName());
		builder.setContentText(message.getMessage());
		builder.setSmallIcon(R.drawable.ic_launcher);

		String imageURL = currentUser.getAccountServer() + messageAuthor.getImageURL();

		// get sender image
		Bitmap userImage = ImageManager.getDefaultInstance(this).imageLoader
		        .getAlreadyDownloadedBitmap(imageURL);

		if (userImage != null) {
			builder.setLargeIcon(userImage);
		}

		// Action intent after clicking the notification
		Intent resultIntentNotification = new Intent(this, ChatActivity.class);

		UserStub currentUserStub = new UserStub();

		currentUserStub.setId(currentUser.getId());
		currentUserStub.setDisplayName(currentUser.getDisplayName());
		currentUserStub.setImageURL(currentUser.getImageURL());

		resultIntentNotification.putExtra(ChatActivity.EXTRA_CURRENT_USER_STUB, currentUserStub);
		resultIntentNotification.putExtra(ChatActivity.EXTRA_USER_STUB, messageAuthor);

		TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

		// Adds the back stack for the Intent (but not the Intent itself)
		stackBuilder.addParentStack(ChatActivity.class);
		// Adds the Intent that starts the Activity to the top of the stack
		stackBuilder.addNextIntent(resultIntentNotification);

		PendingIntent resultPendingIntentNotification = stackBuilder.getPendingIntent(0,
		        PendingIntent.FLAG_UPDATE_CURRENT);

		builder.setContentIntent(resultPendingIntentNotification);

		Notification notification = builder.build();

		NotificationManager manager = (NotificationManager) this
		        .getSystemService(Context.NOTIFICATION_SERVICE);
		manager.notify(13, notification);

		PendingMessagesManager.getDefaultInstance().appendPendingMessage(message);

		FLLogger.d(TAG, "Notification sent");
	}

	// }}

}
